/*
 * timer.c
 *
 *  Created on: Apr 16, 2015
 *      Author: mxw
 */
#include "timer.h"
#include <commlib.h>
#include <S3C2440addr.h>
#include <eventQueue.h>

#define TIME2_INT_MASK (1 << 12)
#define PRESCALER0_MASK (0xFF)
#define PRESCALER1_MASK (0xFF << 8)
#define MUX2_MASK (0xF << 8)
#define MUX3_MASK (0xF << 12)

#define TIMER_MAX_CNT  (32)

#define ST_TIMER_INVILAD     0
#define ST_TIMER_SLEEP      1
#define ST_TIMER_ACTIVE     2

typedef struct {
    unsigned short      tid;
    unsigned char       st;
    ON_TIME_OUT_FUNC    fn;
    unsigned long       interval_in_ms;
    unsigned long       count_of_elapse;
} TIMER_ITEM;
TIMER_ITEM _timer_arr[TIMER_MAX_CNT];

static void set_prescaler() {
    //set prescaler0(Timer 0 and 1) = 0
    rTCFG0 &= ~PRESCALER0_MASK;
    
    //set prescaler1(Timer 2, 3 and 4) = 0
    rTCFG0 &= ~PRESCALER1_MASK;
}

//set hard t2 timer as user soft timer's source@12500Hz => 1clock=
static void init_usr_timer() {
    int i = 0;
    my_memset(_timer_arr, 0, sizeof(_timer_arr));
    for (i = 0; i < TIMER_MAX_CNT; ++i) {
        _timer_arr[i].tid = i;
    }

    //set clock devider as 2(TCFG1[11:8]=0)
    rTCFG1 &= ~MUX2_MASK;
    //rTCFG1 |= (0 << 8);
    
    //now timer2's clcok = PCLK / (prescaler1 + 1) / clock_devider = 50MHz/1/2=25MHz, 1clock=0.4us


    //set count buffer2 and count compare buffer2
    rTCNTB2 = 25000; //timer out time = 25000*0.4us=10ms
    rTCMPB2 = 100; //The value of TCMPBn is used for pulse width modulation (PWM). The timer control logic changes the output level
                   //when the down-counter value matches the value of the compare register in the timer control logic. Therefore, the
                   //compare register determines the turn-on time (or turn-off time) of a PWM output.
    //When the down counter reaches zero, the timer interrupt request is generated to 
    // inform the CPU that the timer operation has been completed.

    rTCON &= ~(0xF << 12);
    rTCON |= (0xB << 12);//auto load, update TCNTB2 TCMPB2, start Timer2
    rTCON &= ~(1 << 13); //clear manual bit.

    rINTMSK &= ~TIME2_INT_MASK; //clear INT_TIME2 mask
}


void init_timer() {
    set_prescaler();
    init_usr_timer();
}

//we use timer3 for sonar  
void config_sonar_timer() { //
    //set timer3 clock devider as 2(TCFG1[15:12]=0)
    rTCFG1 &= ~MUX3_MASK;
    rTCFG1 |= (0 << 12);
    
    //now timer3's clcok = PCLK / (prescaler1 + 1) / clock_devider = 50MHz/1/2=25MHz, 1clock=0.4us

    //set count buffer2 and count compare buffer2
    rTCNTB3 = 65535; //12500/125=10ms
    rTCMPB3 = 100;

    rTCON &= ~(0xF << 12);
    rTCON |= (0xB << 12);//auto load, update TCNTB2 TCMPB2, start Timer2
    rTCON &= ~(1 << 13); //clear manual bit.

    rINTMSK &= ~TIME2_INT_MASK; //clear INT_TIME2 mask
}

unsigned short get_new_timer(ON_TIME_OUT_FUNC fn) {
    int i = 0;
    for (i = 0; i < TIMER_MAX_CNT; ++i) {
        if (ST_TIMER_INVILAD == _timer_arr[i].st) {
            _timer_arr[i].st = ST_TIMER_SLEEP;
            _timer_arr[i].fn = fn;
            _timer_arr[i].count_of_elapse = 0;
            return i;
        }
    }
    return ERR_TIMER_ID;
}

void delete_timer(unsigned short tid) {
    if (tid < TIMER_MAX_CNT) {
        _timer_arr[tid].st = ST_TIMER_INVILAD;
        _timer_arr[tid].count_of_elapse = 0;
        _timer_arr[tid].fn = 0;
    }
}

void start_timer(unsigned short tid, unsigned long interval_in_ms) {
    if (ST_TIMER_INVILAD != _timer_arr[tid].st) {
        _timer_arr[tid].interval_in_ms = interval_in_ms;
        _timer_arr[tid].st = ST_TIMER_ACTIVE;
    }
}

void stop_timer(unsigned short tid) {
    if (ST_TIMER_INVILAD != _timer_arr[tid].st) {
        _timer_arr[tid].count_of_elapse = 0;
        _timer_arr[tid].st = ST_TIMER_SLEEP;
    }
}

void on_timer_loop() {
    int i = 0;
    Queue_Element e;

    for (i = 0; i < TIMER_MAX_CNT; ++i) {
        if (ST_TIMER_ACTIVE == _timer_arr[i].st) {
            _timer_arr[i].count_of_elapse += 10;
            if (_timer_arr[i].count_of_elapse >= _timer_arr[i].interval_in_ms) {
                if (0 != _timer_arr[i].fn) {
                    e.cmd = CMD_TIMER_TIMEOUT;
                    e.data[0] = i;
                    push_event(&g_Main_Queue, &e);
                    _timer_arr[i].count_of_elapse = 0;
                }
            }
        }
    }
}

void call_timer_func(unsigned short tid) {
    if (0 != _timer_arr[tid].fn) {
       _timer_arr[tid].fn(tid);
   }
}
